<template>
    <div
        :class="classes"
        v-click-outside.capture="onClickOutside"
        v-click-outside:mousedown.capture="onClickOutside"
        v-click-outside:touchstart.capture="onClickOutside"
    >
        <div
            :class="selectionCls"
        >
            <input
                :maxlength="maxlength"
                class="input-dropdown ivu-input"
                :class="inputCls"
                ref="inputDropdown"
                type="text"
                :disabled="disabled"
                :placeholder="placeholder"
                v-model="keyword"
                @input="debouncedQueryChange"
                @focus="handleFocus"
                @blur="handleBlur"
                @keydown.tab="visible = false"
                @keydown.esc.stop.prevent="visible = false"
                @keydown.down.prevent="navigateOptions('next')"
                @keydown.up.prevent="navigateOptions('prev')"
                @keyup.enter="handleKeyupEnter"
                autocomplete="off"
            />
        </div>
        <transition name="transition-drop">
            <div
                class="ivu-select-dropdown"
                :style="dropStyles"
                ref="dropdown"
                v-show="dropVisible"
            >
                <ul :class="prefixCls + '-dropdown-list'">
                    <li
                        :class="{
                            'ivu-select-item': true,
                            'ivu-item-focus': hoverIndex === index,
                            'ivu-select-item-disabled': item.disabled,

                        }"
                        v-for="(item, index) in options"
                        :key="item[valueKey]"
                        @click="handleClick(item, index)"
                    >
                        {{item | getOptionLables(displayField)}}
                    </li>
                </ul>
            </div>
        </transition>
    </div>
</template>
<script>
import { directive as clickOutside } from 'v-click-outside-x'
import Popper from 'popper.js'
import '@/view/components/RemoteSearch/myInputComponents.less'
import { trim, debounce } from '@/libs/tools'
/**
 * 获取下拉选项展示的内容
 * @param {Object} data 下拉选项的值
 * @param {Array} keys 需要展示的keys数组集合
 * @param {String} flag 分割符号
 * @returns {String}
 */
function getOptionLables (data, keys, flag = ' ') {
    return keys
        .map(key => {
            return data[key] || ''
        })
        .join(flag)
}
const prefixCls = 'ivu-select'
export default {
    directives: { clickOutside },
    props: {
        value: {
            default: ''
        },
        label: {
            type: String,
            default: ''
        },
        valueKey: {
            default: 'value',
            type: String
        },
        labelKey: {
            default: 'label',
            type: String
        },
        // 显示字段
        displayField: {
            default: () => ['label'],
            type: Array
        },
        // 大小
        size: {
            validator (value) {
                return ['small', 'large', 'default'].indexOf(value) > -1
            },
            default: 'default'
        },
        // 输入提示
        placeholder: {
            type: String,
            default: ''
        },
        // 是否禁用
        disabled: {
            type: Boolean,
            default: false
        },
        // 最大输入长度
        maxlength: {
            type: [Number, String],
            default: ''
        },
        // 默认展示的下拉选项
        defaultOptions: {
            type: Array,
            default: () => []
        },
        /**
         * 远程搜索方法 搜到的结果list作为参数传入cb回调函数中
         * @param {String} v 搜索关键字
         * @param {Function} cb 回调函数
         */
        remoteMethod: {
            type: Function,
            default: (v, cb) => {}
        },
        // change 时 是否返回一个带有label的对象
        labelInValue: {
            type: Boolean,
            default: false
        },
        // 下拉选项位置
        placement: {
            type: String,
            default: 'bottom-start'
        },
        // 下拉选项宽度 单位 px
        dropdownWidth: {
            type: [Number, String],
            default: ''
        }
    },
    data () {
        return {
            prefixCls,
            visible: false,
            isFocused: false,
            hoverIndex: -1,
            keyword: '',
            options: [],
            width: '',
            selectValue: '' // 当前选择的值
        }
    },
    computed: {
        classes () {
            return [
                `${prefixCls}`,
                {
                    [`${prefixCls}-disabled`]: this.disabled,
                    [`${prefixCls}-${this.size}`]: !!this.size
                }
            ]
        },
        inputCls () {
            return [
                {
                    [`ivu-input-${this.size}`]: !!this.size
                }
            ]
        },
        selectionCls () {
            return {
                'ivu-input-wrapper': true,
                [`${prefixCls}-selection-focused`]: this.isFocused
            }
        },
        dropStyles () {
            let style = {}
            if (this.width) style.minWidth = `${this.width}px`
            return style
        },

        // 根据 visible 和 options 的长度来展示 下拉选项列表
        dropVisible () {
            return this.visible && this.options.length > 0
        }
    },
    watch: {
        value (v) {
            if (v !== this.selectValue) {
                this.selectValue = v
            }
            this.dispatch('FormItem', 'on-form-change', val)
        },
        label (v) {
            if (v !== this.keyword) {
                this.keyword = v
            }
        },
        // 监听 isFocused 来改变 input 的focus  blur
        isFocused (focused) {
            const el = this.$refs.inputDropdown
            if (el) {
                el[focused ? 'focus' : 'blur']()
            }
        },

        // 鼠标点击其他地方的时候 visible  设置false
        // esc  tab  enter 键盘事件设置 false
        // 上下移动的时候设置 true
        // 搜索的时候 设置 true
        visible (v) {
            if (v) {
                this.updatePopper()
            } else {
                this.destroyPopper()
            }
        },
        // 设置滚动条
        hoverIndex (index) {
            if (index < 0 || this.options.length <= 0) return
            // update scroll
            const ulEle = this.$refs.dropdown
            const optionInstance = ulEle.getElementsByClassName('ivu-select-item')[index]
            if (optionInstance) {
                let bottomOverflowDistance =
                    optionInstance.getBoundingClientRect().bottom -
                    ulEle.getBoundingClientRect().bottom
                let topOverflowDistance =
                    optionInstance.getBoundingClientRect().top -
                    ulEle.getBoundingClientRect().top
                if (bottomOverflowDistance > 0) {
                    ulEle.scrollTop += bottomOverflowDistance
                }
                if (topOverflowDistance < 0) {
                    ulEle.scrollTop += topOverflowDistance
                }
            }
        }
    },
    created () {
        this.debouncedQueryChange = debounce(this.handleQueryChange, 0)
        this.selectValue = this.value || ''
        this.keyword = this.label || ''
        this.options = this.defaultOptions.slice(0, 15)
    },
    methods: {
        // 切换下拉列表 状态
        toggleMenu (e, force) {
            this.visible = typeof force !== 'undefined' ? force : !this.visible
        },
        // 隐藏下拉列表
        hideMenu () {
            this.toggleMenu(null, false)
        },
        // 外部点击隐藏下拉列表
        onClickOutside (event) {
            this.visible = false
        },
        // 选项上下移动
        navigateOptions (direction) {
            this.visible = true
            if (direction === 'next') {
                this.hoverIndex++
                if (this.hoverIndex >= this.options.length) {
                    this.hoverIndex = 0
                }
            } else if (direction === 'prev') {
                this.hoverIndex--
                if (this.hoverIndex < 0) {
                    this.hoverIndex = this.options.length - 1
                }
            }
        },

        // 处理回车事件
        handleKeyupEnter (event) {
            if (this.visible) {
                this.visible = false
                const v = trim(event.target.value)
                // 判断搜索的内容是否在选项列表中
                const value = v.toUpperCase()
                const index = this.options.findIndex(item => {
                    return getOptionLables(item, this.displayField, ',')
                        .toUpperCase()
                        .includes(value)
                })
                // 不存在则重新搜索
                if (index < 0) {
                    this.handleRemoteSearch(v, list => {
                        this.hoverIndex = -1
                        if (list.length) {
                            // 默认选上第一个
                            this.selectItem(list[0], 0)
                        } else {
                            // 没有则清空
                            this.selectValue = ''
                            this.$emit('input', '')
                            this.$emit('change', null)
                        }
                    })
                } else {
                    // 存在则判断 当前选中的 序号 并直接选择
                    const hoverOption =
                        (this.hoverIndex > -1 && this.options[this.hoverIndex]) || ''
                    if (hoverOption) {
                        // 如果存在 选项 则直接选中 （输入的搜索内容改变时判断过是否清空）
                        this.selectItem(hoverOption, this.hoverIndex)
                    }
                }
            }
            this.$emit('on-enter', event)
        },

        /**
         * 处理点击选项事件
         * @param {Object} option
         * @param {Number} index
         */
        handleClick (option, index) {
            this.selectItem(option, index)
            // 隐藏下拉框
            this.hideMenu()
            if (!this.disabled) {
                this.$nextTick(() => {
                    // 聚焦input
                    this.$refs.inputDropdown.focus()
                })
            }
        },
        selectItem (option, index) {
            this.hoverIndex = index
            this.selectValue = option[this.valueKey]
            this.$emit('input', option[this.valueKey])
            this.$emit(
                'change',
                this['labelInValue'] ? option : option[this.valueKey]
            )
            this.keyword = this.options[index][this.labelKey]
            this.$emit('get-query-name', trim(this.keyword))
        },

        /**
         * 处理input  focus 事件
         */
        handleFocus (event) {
            event.target.select()
            this.$emit('on-focus', event)
            this.isFocused = true
        },

        /**
         * 处理 input blur  事件
         */
        handleBlur (event) {
            this.$emit('get-query-name', trim(this.keyword))
            this.$emit('on-blur', false)
            this.isFocused = false
            this.dispatch('FormItem', 'on-form-blur', this.value)
        },

        /**
         * 处理远程搜索事件
         */
        handleRemoteSearch (v, cb) {
            this.remoteMethod(v, data => {
                const list = data && data.length ? data : []
                const oldLen = this.options.length
                const len = list.length

                this.options = list
                cb && cb(list)

                // 当 dropdown 在控件上部显示时，如果选项列表的长度由外部动态变更了，
                // dropdown 的位置会有点问题，需要重新计算
                if (oldLen !== len) {
                    this.updatePopper()
                }
            })
        },
        resetTransformOrigin () {
            if (!this.popper) return
            let x_placement = this.popper.popper.getAttribute('x-placement')
            let placementStart = x_placement.split('-')[0]
            let placementEnd = x_placement.split('-')[1]
            const leftOrRight =
                x_placement === 'left' || x_placement === 'right'
            if (!leftOrRight) {
                this.popper.popper.style.transformOrigin =
                    placementStart === 'bottom' ||
                    (placementStart !== 'top' && placementEnd === 'start')
                        ? 'center top'
                        : 'center bottom'
            }
        },
        // 更新 Popper 插件
        updatePopper () {
            if (this.popper) {
                this.$nextTick(() => {
                    this.popper.update()
                })
            } else {
                this.$nextTick(() => {
                    this.popper = new Popper(
                        this.$refs.inputDropdown,
                        this.$refs.dropdown,
                        {
                            placement: this.placement,
                            modifiers: {
                                computeStyle: {
                                    gpuAcceleration: false
                                },
                                preventOverflow: {
                                    boundariesElement: 'window'
                                }
                            },
                            onCreate: () => {
                                this.resetTransformOrigin()
                                this.$nextTick(this.popper.update())
                            },
                            onUpdate: () => {
                                this.resetTransformOrigin()
                            }
                        }
                    )
                })
            }
            const element = this.$refs.inputDropdown
            if (element) {
                this.width = parseInt(element.getBoundingClientRect().width)
            }
        },

        // 销毁 Popper 插件
        destroyPopper () {
            if (this.popper) {
                this.$nextTick(() => {
                    this.popper.destroy()
                    this.popper = null
                })
            }
        },
        // 处理搜索内容改变
        handleQueryChange (event) {
            if (event.target.value === this.keyword) {
                const v = trim(this.keyword)
                if (v) {
                    // 清空上下移动的选项
                    this.hoverIndex = -1

                    // 远程搜索内容
                    this.handleRemoteSearch(v, () => {
                        if (this.options.length) {
                            this.hoverIndex = 0
                        }
                    })
                } else {
                    this.selectValue = ''
                    this.$emit('input', '')
                    this.$emit('change', null)
                    this.options = this.defaultOptions.slice()
                }
                if (this.options.length) {
                    this.hoverIndex = 0
                }
                // 如果隐藏了下拉列表 则需要展示
                if (this.isFocused && !this.visible) {
                    this.visible = true
                }
            }
        },
        dispatch (componentName, eventName, params) {
            let parent = this.$parent || this.$root
            let name = parent.$options.name

            while (parent && (!name || name !== componentName)) {
                parent = parent.$parent

                if (parent) {
                    name = parent.$options.name
                }
            }
            if (parent) {
                parent.$emit.apply(parent, [eventName].concat(params))
            }
        }
    },
    filters: {
        getOptionLables
    },
    beforeDestroy () {
        if (this.popper) {
            this.popper.destroy()
        }
    }
}
</script>
<style lang="less" scoped>
.ivu-select-item {
    transition: none;
    &:hover {
        background: #CCE4FC;
    }
    &.ivu-item-focus {
        background: #1177e8;
        color: #fff;
    }
}

</style>
